/**
 * Copyright 2009 University of  Washington
 * This code is released under the GPL version 3 - with a copy of the license included
 * @author Maya Hao Li
 *
 */
package seedpod.model.rdb;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import seedpod.rdb.RdbSql;
import seedpod.rdb.SqlUtil;
import seedpod.util.XMLWriter;


public class Attribute  {

	private ArrayList<String> _allowedValues = new ArrayList<String>();
	private String _comment = "";
	private String _databaseParam;
	private boolean _isUnique = false;
	private String _viewWidget;
	private String _viewWidgetParameter;
	private String _widget;
	private String _widgetParameter;
	protected String _defaultValue = null;
	protected Attribute _inverseAttribute; // TODO implement
	protected boolean _isPrimaryKey = false; // TODO remove foreign key as a
	// type from databaseType
	protected boolean _isRequired = false;
	protected Number _maximumValue;
	protected Number _minimumValue;
	protected String _name;
	protected Rdb _rdb;
	protected Relation _relation = null;
	protected String _relName = null;
	protected RdbValueType _type;
	protected Integer _valueLength = null;
	protected boolean _isImplemented = true;
	protected Float _viewSequence;

	public Attribute(Rdb schema, Relation rel, String attributeName, RdbValueType type) {
		_name = attributeName;
		_type = type;
		_rdb = schema;
		if ( rel != null ) {
			_relation = rel;
			_relName = rel.getName();
		}
	}

	public Attribute(Rdb schema, Relation containerRelation, String attributeName, RdbValueType type,
			boolean isRequired, String defaultValue) throws NoRelationFoundException {
		this( schema, containerRelation.getName(), attributeName, type );
		setIsRequired( isRequired );
		setDefaultValue( defaultValue );
	}

	public Attribute(Rdb schema, String containerRelName, String attributeName, RdbValueType type) throws NoRelationFoundException {
		_name = attributeName;
		_type = type;
		_rdb = schema;
		if ( containerRelName != null ) {
			_relation = _rdb.getRelation( containerRelName );
			_relName = containerRelName;
		}

	}

	public Attribute(Relation containerTbl, String attributeName, RdbValueType type, boolean isRequired,
			String defaultValue) {
		if ( containerTbl != null ) {
			_relName = containerTbl.getName();
			_relation = containerTbl;
		}
		_name = attributeName;
		_rdb = null;
		_type = type;
		_isRequired = isRequired;
		_defaultValue = defaultValue;
	}

	public int compareTo( Object anotherAtt ) {
		return getName().compareTo( ((Attribute)anotherAtt).getName() );
	}

	public boolean equals( Object anotherAtt ) {
		return getName().equals( ((Attribute)anotherAtt).getName() );
	}

	public Collection<String> getAllowedValues() {
		if ( _allowedValues.size() > 0 )
			return _allowedValues;
		else
			return null;
	}

	public String getComment() {
		return (_comment != null && _comment.length() != 0) ? ("-- " + _comment) : "";
	}

	public String getContainerRelationName() {
		return _relName;
	}

	public String getDatabaseParameter() {
		return (_databaseParam == null) ? "" : _databaseParam;
	}

	public RdbValueType getRdbValueType() {
		return _type;
	}
	
	public Integer getRdbValueLength() {
		Integer length = null;
		if ( _type.equals( RdbValueType.VARCHAR ) ) {
			String dbParam = getDatabaseParameter();
			if ( dbParam != null && dbParam.length() > 0 )
				length = Integer.decode(dbParam);
		}
		return length;
	}


	public String getDatabaseTypeString() {
		String s = "";

		 if ( _type.equals( RdbValueType.VARCHAR ) ) {
			s += _type.toString();
			if ( getDatabaseParameter().length() > 0 )
				s += SqlUtil.PARAN( getDatabaseParameter() );
		} else
			s += _type.toString();

		return s;
	}

	public String getDefaultValue() {
		RdbValueType type = getRdbValueType();

		// create a default val if there is non defined and attribute is not a key
		if ( (_defaultValue == null || _defaultValue.length() == 0) && !isRequired() && !isUnique() && !isPrimaryKey()
				&& !isForeignKey() ) {
			/*if ( type.equals( RdbValueType.INTEGER ) || type.equals( RdbValueType.NUMERIC ) )
				_defaultValue = String.valueOf( 0 );
			else*/ 
			if ( type.equals( RdbValueType.VARCHAR ) || type.equals( RdbValueType.TEXT )
					|| type.equals( RdbValueType.CHARACTER ) )
				_defaultValue = "NULL";
			else if ( type.equals( RdbValueType.BOOLEAN ) )
				_defaultValue = "FALSE";
			setComment( "AUTO generated default value" );
		} else if ( type.equals( RdbValueType.SERIAL ) )
			_defaultValue = RdbSql.DEFAULT;

		return _defaultValue;
	}

	public Attribute getInverseAttribute() {
		return _inverseAttribute;
	}

	public Number getMaximumValue() {
		return _maximumValue;
	}

	public Number getMinimumValue() {
		return _minimumValue;
	}

	public String getName() {
		return _name;
	}

	public Relation getRelation() {
		return _relation;
	}

	public String getViewWidget() {
		return _viewWidget;
	}

	public String getViewWidgetParameter() {
		return _viewWidgetParameter;
	}

	public String getWidget() {
		return _widget;
	}

	public String getWidgetParameter() {
		return _widgetParameter;
	}
	
	public Float getViewSequence() {
		return _viewSequence;
	}

	public int hashCode() {
		return (31 + getName().hashCode());
	}

	public boolean isForeignKey() {
		return _type.equals( RdbValueType.FOREIGN_KEY );
	}

	public boolean isPrimaryKey() {
		return _isPrimaryKey;
	}

	public boolean isRequired() {
		return _isRequired;
	}

	public boolean isUnique() {
		return _isUnique;
	}

	public boolean isImplemented() {
		return _isImplemented;
	}

	public XMLWriter serializeXML( XMLWriter out ) throws XMLWriter.XMLException {
		XMLWriter attribute = out.addElement( "attribute" );
		attribute.addElement( "name", _name );
		attribute.addElement( "type", _type );

		if ( _allowedValues.size() > 0 ) {
			XMLWriter av = attribute.addElement( "allowedValues" );
			for ( int i = 0; i < _allowedValues.size(); i++ )
				av.addElement( "allowedValue_" + i, _allowedValues.get( i ) );
		}
		return attribute;
	}

	/**
	 * sets the allowed values for symbol or varchar type only. (Instance or Class types are done with other methods)
	 * 
	 * @param allowedValues
	 */
	public void setAllowedValues( Collection<String> allowedValues ) {
		for ( Iterator<String> avIter = allowedValues.iterator(); avIter.hasNext(); )
			_allowedValues.add( avIter.next() );
	}

	public void setComment( String c ) {
		_comment = c;
	}

	public void setContainerRelation( Relation relation ) {
		_relation = relation;
		_relName = relation.getName();

	}

	public void setDatabaseParameter( int i ) {
		_databaseParam = String.valueOf( i );
	}

	public void setDatabaseTypeParameter( String param ) {
		_databaseParam = param;
	}

	public void setDatabaseType( RdbValueType type ) {
		_type = type;
	}

	public void setDefaultValue( String val ) {
		_defaultValue = val;
	}

	public void setFormWidget( String widget ) {
		_widget = widget;
	}

	public void setFormWidgetParameter( String param ) {
		_widgetParameter = param;
	}

	public void setInverse( Attribute a ) {
		_inverseAttribute = a;
	}

	public void setIsPrimaryKey( boolean primaryKey ) {
		_isPrimaryKey = primaryKey;
	}

	public void setIsRequired( boolean notNull ) {
		_isRequired = notNull;
	}

	public void setIsUnique( boolean b ) {
		_isUnique = b;
	}

	public void setMaximumValue( Number max ) {
		_maximumValue = max;
	}

	public void setMinimumValue( Number min ) {
		_minimumValue = min;
	}

	public void setName( String name ) {
		_name = name;
	}

	public void setViewWidget( String viewWidget ) {
		_viewWidget = viewWidget;
	}

	public void setViewWidgetParameter( String viewParam ) {
		_viewWidgetParameter = viewParam;
	}

	public void setIsImplemented( boolean assoc ) {
		_isImplemented = assoc;
	}

	public void setViewSequence( Float viewSequence ) {
		_viewSequence = viewSequence;
	}


}